"""
Tests for Discussion API internal interface
"""

from unittest import mock

import ddt
import pytest
from django.contrib.auth import get_user_model
from django.test.client import RequestFactory
from opaque_keys.edx.keys import CourseKey

from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

from common.djangoapps.student.tests.factories import (
    UserFactory
)
from lms.djangoapps.discussion.rest_api.api import get_user_comments
from lms.djangoapps.discussion.rest_api.tests.utils import (
    ForumMockUtilsMixin,
    make_minimal_cs_comment,
)
from openedx.core.lib.exceptions import CourseNotFoundError, PageNotFoundError

User = get_user_model()


@ddt.ddt
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
class GetUserCommentsTest(ForumMockUtilsMixin, SharedModuleStoreTestCase):
    """
    Tests for get_user_comments.
    """

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        super().setUpClassAndForumMock()

    @classmethod
    def tearDownClass(cls):
        super().tearDownClass()
        super().disposeForumMocks()

    @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
    def setUp(self):
        super().setUp()

        self.course = CourseFactory.create()

        # create staff user so that we don't need to worry about
        # permissions here
        self.user = UserFactory.create(is_staff=True)
        self.register_get_user_response(self.user)

        self.request = RequestFactory().get(f'/api/discussion/v1/users/{self.user.username}/{self.course.id}')
        self.request.user = self.user

    def test_call_with_single_results_page(self):
        """
        Assert that a minimal call with valid inputs, and single result,
        returns the expected response structure.
        """
        self.register_get_comments_response(
            [make_minimal_cs_comment()],
            page=1,
            num_pages=1,
        )
        response = get_user_comments(
            request=self.request,
            author=self.user,
            course_key=self.course.id,
        )
        assert "results" in response.data
        assert "pagination" in response.data
        assert response.data["pagination"]["count"] == 1
        assert response.data["pagination"]["num_pages"] == 1
        assert response.data["pagination"]["next"] is None
        assert response.data["pagination"]["previous"] is None

    @ddt.data(1, 2, 3)
    def test_call_with_paginated_results(self, page):
        """
        Assert that paginated results return the correct pagination
        information at the pagination boundaries.
        """
        self.register_get_comments_response(
            [make_minimal_cs_comment() for _ in range(30)],
            page=page,
            num_pages=3,
        )
        response = get_user_comments(
            request=self.request,
            author=self.user,
            course_key=self.course.id,
            page=page,
        )
        assert "pagination" in response.data
        assert response.data["pagination"]["count"] == 30
        assert response.data["pagination"]["num_pages"] == 3

        if page in (1, 2):
            assert response.data["pagination"]["next"] is not None
            assert f"page={page+1}" in response.data["pagination"]["next"]
        if page in (2, 3):
            assert response.data["pagination"]["previous"] is not None
            assert f"page={page-1}" in response.data["pagination"]["previous"]
        if page == 1:
            assert response.data["pagination"]["previous"] is None
        if page == 3:
            assert response.data["pagination"]["next"] is None

    def test_call_with_invalid_page(self):
        """
        Assert that calls for pages that exceed the existing number of
        results pages raise PageNotFoundError.
        """
        self.register_get_comments_response([], page=2, num_pages=1)
        with pytest.raises(PageNotFoundError):
            get_user_comments(
                request=self.request,
                author=self.user,
                course_key=self.course.id,
                page=2,
            )

    def test_call_with_non_existent_course(self):
        """
        Assert that calls for comments in a course that doesn't exist
        result in a CourseNotFoundError error.
        """
        self.register_get_comments_response(
            [make_minimal_cs_comment()],
            page=1,
            num_pages=1,
        )
        with pytest.raises(CourseNotFoundError):
            get_user_comments(
                request=self.request,
                author=self.user,
                course_key=CourseKey.from_string("course-v1:x+y+z"),
                page=2,
            )
